/**
 * 数据/配置查询协助工具
 * @type DataStructure 数据结构
 */
export abstract class ConfigHelper<DataStructure>{
    protected _path:string;
    protected _data:DataStructure;

    constructor(path?:string){
        this._path = path;
        // path && this.loadData(null, null, path);
    }
    
    /**
     * 直接设置数据
     * @param data 
     * @returns 
     */
    setData(data:DataStructure):ConfigHelper<DataStructure>{
        this._data = data;
        return this;
    }

    /** 
     * 数据
     */
    get data():DataStructure{
        return this._data;
    }

    /**
     * 加载配置（需要在子类中实现）
     * @param onComplete 
     * @param caller 
     * @param path 
     * @returns 
     */
    abstract loadData(onComplete?:(error:Error, config:ConfigHelper<DataStructure>)=>void, caller?:any, path?: string):ConfigHelper<DataStructure>;

    get path():string{return this._path}

    /**
     * 获取字段对应的数据
     * @param key 字段名,支持'att.subatt.subatt...'多层字段
     * @returns 
     */
    findData<T>(key:string|number):T{
        return ConfigHelper.findData<T>(this._data, key);
    }

    /**
     * 通过指定条件筛选并返回第一个子项
     * @param filterKey 子项拥有的字段名
     * @param filterValue 子项的key字段的值等于value
     * @param subKey 目标数据路径,支持'att.subatt.subatt...'多层字段, 默认为根数据
     * @returns 匹配结果为0个的情况下返回Null
     */
    firstData<T>(filterKey:string, filterValue:any, subKey?:string|number):T{
        return ConfigHelper.firstData(this._data, filterKey, filterValue, subKey);
    }

    /**
     * 通过指定条件筛选并返回最后一个子项
     * @param filterKey 子项拥有的字段名
     * @param filterValue 子项的key字段的值等于value
     * @param subKey 目标数据路径,支持'att.subatt.subatt...'多层字段, 默认为根数据
     * @returns 匹配结果为0个的情况下返回Null
     * 
     * 关于顺序：先以升序枚举数值键，然后以插入顺序枚举字符串和符号键
     */
    lastData<T>(filterKey:string, filterValue:any, subKey?:string|number):T{
        return ConfigHelper.lastData(this._data, filterKey, filterValue, subKey);
    }

    /**
     * 通过指定条件筛选子项. 比如查询道具类型(type)为1的道具列表.比如章节(chapter)为第1章的关卡列表.
     * @param filterKey 子项拥有的字段名
     * @param filterValue 子项的key字段的值等于value
     * @param subKey 目标数据路径,支持'att.subatt.subatt...'多层字段, 默认为根数据
     * @returns 符合条件的子项数组.
     */
    selectData<T>(filterKey:string, filterValue:any, subKey?:string|number):T[]{
        return ConfigHelper.selectData<T>(this._data, filterKey, filterValue, subKey);
    }

    /**
     * 索引数据位置,-1为不存在
     * @param value 查找的值
     * @param key [可选] 子数据字段名,支持'att.subatt.subatt...'多层字段, 默认为根数据
     * @returns -1不存在, >=0为值所在的位置
     */
    indexof<T>(value:T, key?:string|number):number{
        return ConfigHelper.indexof<T>(this._data, value, key);
    }

    /**
     * 最后索引数据位置,-1为不存在
     * @param value 查找的值
     * @param key [可选] 子数据字段名,支持'att.subatt.subatt...'多层字段, 默认为根数据
     * @param fromIndex [可选] 开始检索的索引
     * @returns -1不存在, >=0为值所在的位置
     */
    lastIndexof<T>(value:T, key?:string|number, fromIndex?:number):number{
        return ConfigHelper.lastIndexof<T>(this._data, value, key, fromIndex);
    }

    /**
     * 获取数据数量,取数据的length或者size,可以自定义或者配置为数组
     * @returns 
     */
    get size():number{
        return this._data['length'] || this._data['size'];
    }

    /**
     * 获取字段对应的数据
     * @param data 要查询的数据集
     * @param key 字段名,支持'att.subatt.subatt...'多层字段
     * @returns 
     */
    static findData<T>(data:any, key:string|number):T{
        let result:any = data;
        if(typeof key == 'number'){
            result = result[key];
        }else{
            let keys:string[] = key.split('.');
            for (let i = 0; i < keys.length; i++) {
                const element = keys[i];
                if(!result[element]){
                    return undefined;
                }
                result = result[element];
            }
        }

        return result;
    }

    /**
     * 通过指定条件筛选并返回第一个子项
     * @param data 要查询的数据集
     * @param filterKey 子项拥有的字段名
     * @param filterValue 子项的key字段的值等于value
     * @param subKey 目标数据路径,支持'att.subatt.subatt...'多层字段, 默认为根数据
     * @returns 匹配结果为0个的情况下返回Null
     * 
     * 关于顺序：先以升序枚举数值键，然后以插入顺序枚举字符串和符号键
     */
    static firstData<T>(data:any, filterKey:string, filterValue:any, subKey?:string|number):T{
        data = subKey ? ConfigHelper.findData(data, subKey) : data;
        //以固定顺序访问（先以升序枚举数值键，然后以插入顺序枚举字符串和符号键），forin在不同运行环境下顺序不固定。
        let keys = Object.getOwnPropertyNames(data);
        for (let i = 0; i < keys.length; i++) {
            const key = keys[i];
            const element = data[key];
            if(element[filterKey] == filterValue){
                return element;
            }
        }
        return null;
    }

    /**
     * 通过指定条件筛选并返回最后一个子项
     * @param data 要查询的数据集
     * @param filterKey 子项拥有的字段名
     * @param filterValue 子项的key字段的值等于value
     * @param subKey 目标数据路径,支持'att.subatt.subatt...'多层字段, 默认为根数据
     * @returns 匹配结果为0个的情况下返回Null
     * 
     * 关于顺序：先以升序枚举数值键，然后以插入顺序枚举字符串和符号键
     */
        static lastData<T>(data:any, filterKey:string, filterValue:any, subKey?:string|number):T{
        data = subKey ? ConfigHelper.findData(data, subKey) : data;
        //以固定顺序访问（先以升序枚举数值键，然后以插入顺序枚举字符串和符号键），forin在不同运行环境下顺序不固定。
        let keys = Object.getOwnPropertyNames(data);
        for (let i = keys.length; i >= 0; i--) {
            const key = keys[i];
            const element = data[key];
            if(element[filterKey] == filterValue){
                return element;
            }
        }
        return null;
    }

    /**
     * 通过指定条件筛选子项. 比如查询道具类型(type)为1的道具列表.比如章节(chapter)为第1章的关卡列表.
     * @param data 要查询的数据集
     * @param filterKey 子项拥有的字段名
     * @param filterValue 子项的key字段的值等于value
     * @param subKey 目标数据路径,支持'att.subatt.subatt...'多层字段, 默认为根数据
     * @returns 符合条件的子项数组.
     * 
     * 关于顺序：先以升序枚举数值键，然后以插入顺序枚举字符串和符号键
     */
    static selectData<T>(data:any, filterKey:string, filterValue:any, subKey?:string|number):T[]{
        let result:T[] = [];
        data = subKey ? ConfigHelper.findData(data, subKey) : data;
        //以固定顺序访问（先以升序枚举数值键，然后以插入顺序枚举字符串和符号键），forin在不同运行环境下顺序不固定。
        let keys = Object.getOwnPropertyNames(data);
        for (let i = 0; i < keys.length; i++) {
            const key = keys[i];
            const element = data[key];
            if(element[filterKey] == filterValue){
                result.push(element);
            }
        }
        return result;
    }

    /**
     * 索引数据位置,-1为不存在
     * @param data 要查询的数据集
     * @param value 查找的值
     * @param key [可选] 子数据字段名,支持'att1.att2.att3...'多层字段, 默认为根数据
     * @returns -1不存在, >=0为值所在的位置
     */
    static indexof<T>(data:any, value:T, key?:string|number):number{
        let result:T[] = key ? ConfigHelper.findData<T>(data, key) : data;
        return result.indexOf(value);
    }

    /**
     * 最后索引数据位置,-1为不存在
     * @param data 要查询的数据集
     * @param value 查找的值
     * @param key [可选] 子数据字段名,支持'att1/att2/att3...'多层字段, 默认为根数据
     * @param fromIndex [可选] 开始检索的索引
     * @returns -1不存在, >=0为值所在的位置
     */
    static lastIndexof<T>(data:any, value:T, key?:string|number, fromIndex?:number):number{
        let result:T[] = key ? ConfigHelper.findData<T>(data, key) : data;
        return result.lastIndexOf(value, fromIndex);
    }
}